
#include "Misc_t.hh"
#include <QtTest/QtTest>
#include "Null.hh"
#include "AutoPtr.hpp"
#include "Debug.hh"
#include "CodeContext.hh"
#include "Utils.hh"
#include "ForAll.hpp"
#include "IfThenElse.hpp"
#include "MemDump.hpp"

using namespace std;
using namespace TC;


namespace {

// For Null tests
  struct NHelper
  {
    void op() {}
    int mem;
  };

// For AutoPtr tests
  int Construction = 0;
  int Destruction = 0;

  struct B 
  {
    B(int v=0) : m_val(v) { ++Construction; }
    virtual ~B() { ++Destruction; }
    virtual int getVal() const { return m_val; }
    int m_val;
  };

  struct D :  public B
  {
    D(int v=1) : B(v) {}
    int getVal() const { return m_val+1; }
  };

// For util_tests()
  struct St {
    char c;
    float f;
    double d;
    int i;
    unsigned u;
  };

}


namespace {

  // See C++Templates book, section 17.3 for this example;
  // It calculates square root of a given integer at compile time
  // using recursion:

  // Primary template for main recursive step
  template<int N, int LO=1, int HI=N>
  class SqrtR {
  public:
    // compute the midpoint, rounded up
    enum { mid = (LO+HI+1)/2 };

    // search a not too large value in a halved interval
    typedef typename IfThenElse<(N<mid*mid),
                                SqrtR<N,LO,mid-1>,
                                SqrtR<N,mid,HI> >::ResultT SubT;
    enum { result = SubT::result };
  };

  // Partial specialization for end of recursion criterion
  template<int N, int S>
  class SqrtR<N, S, S> {
  public:
    enum { result = S };
  };


  // See C++Templates book, section 17.4 for this example;
  // It calculates square root of a given integer at compile time
  // using iteration:

  // Template to yield template argument as result
  template<int N>
  struct SqrtIVal {
    enum { result = N };
  };

  // Template to compute sqrt(N) via iteration
  template <int N, int I=1>
  struct SqrtI {
    // instantiate next step or result type as branch
    typedef typename IfThenElse<(I*I<N),
                                SqrtI<N,I+1>,
                                SqrtIVal<I>
                               >::ResultT
            SubT;

    // use the result of branch type
    enum { result = SubT::result };
  };

}



void Misc_t::null_tests()
{

  int* pi = NullPtr;
  QVERIFY(!pi);

  char* pc = NullPtr;
  QVERIFY(!pc);

  void* pv = NullPtr;
  QVERIFY(!pv);

  NHelper* nh = NullPtr;
  QVERIFY(!nh);

  void (NHelper::* pf)() = &NHelper::op;
  QVERIFY(pf);

  //TODO: Doesn't work. Why?
  pf = 0; // NullPtr;
  QVERIFY(!pf);

  int NHelper::* pm = &NHelper::mem;
  QVERIFY(pm);

  pm = NullPtr;
  QVERIFY(!pm);

}
  

void Misc_t::autoptr_tests()
{
  {
    typedef AutoPtr<B> autoptr_t; 
    B* bptr = 0;
    D* dptr = 0;
    int i;
    
    { // Test normal construction and destruction
      autoptr_t ap(new B);
    }
      
    bptr = new B;
    autoptr_t item(bptr);
    QVERIFY(item.get()==bptr);
    item.reset(new D);
    // Note, released object is D
    bptr = item.release();
    delete bptr;
    QVERIFY(0==item.get());

    item.reset(new B);
    QVERIFY(item.get());

    bptr = new B;
    item = bptr;
    QVERIFY(item.get()==bptr);
      
    dptr = new D;
    item = dptr;
    QVERIFY(item.get()==dptr);
      
    // Test assignment semantics, new object should be owned by item
    for(i=0; i<10; ++i)  
      item = new D;
    // Another way of assigning into the AutoPtr
    item = autoptr_t(new D);


    // Constant auto pointers should be constant
    const autoptr_t citem(new D);
    QVERIFY(citem.get());
    // Shouldn't compile this one:
    //citem = new B;
  }
     
  QVERIFY(Construction==Destruction);
  TC_LOG_DEBUG("Constructions=" << Construction);
  TC_LOG_DEBUG("Destructions=" << Destruction);
}


void Misc_t::codecontext_tests()
{
  TC_CODECONTEXT;
  static CodeContext sctx(__FILE__, __LINE__, TC_FUNCTION);
  CodeContext ctx =  MakeCodeContext(__FILE__, __LINE__);
  QVERIFY(true);
}

void Misc_t::util_tests()
{
  QVERIFY( 0 == TC_OFFSETOF(St,c) );
  
  TC_LOG_DEBUG( "Offset of St.f=" << TC_OFFSETOF(St,f) );
  //QVERIFY( TC_OFFSETOF(St,f)==1 );

  QVERIFY( TC_OFFSETOF(St,f) < TC_OFFSETOF(St,i) );
  
  QVERIFY( TC_OFFSETOF(St,d) < TC_OFFSETOF(St,u) );
  
}
      
void Misc_t::ifthenelse_tests()
{
  QVERIFY(SqrtR<1>::result==1);
  QVERIFY(SqrtR<16>::result==4);
  QVERIFY(SqrtR<25>::result==5);
  QVERIFY(SqrtR<42>::result==6);
  
  QVERIFY(SqrtI<1>::result==1);
  QVERIFY(SqrtI<16>::result==4);
  QVERIFY(SqrtI<25>::result==5);
  QVERIFY(SqrtI<42>::result==7);
}


void Misc_t::memdump_tests()
{
  St st;
  string s = MemDump(st);

  TC_LOG_INFO(s);

  QVERIFY(true);   
}
